# coding=utf8
import numpy as np

# 机器学习中入门算法 K近邻算法的实现
# sklearn的实现 https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/neighbors/classification.py
# [K Nearest Neighbor 算法简介] http://coolshell.cn/articles/8052.html


class Classify:
    def __init__(self, k=5):
        self.k = k
        self.data_label = None
        self.data_mat = None
        self.isNorm = False
        self.min_val = None
        self.sub_val = None

    def set_data_mat(self, data_mat):
        #设置样本数据，一个二维ndarray
        assert isinstance(data_mat, np.ndarray)
        self.data_mat = data_mat

    def set_data_label(self, data_label):
        #设置每一行的标签
        assert isinstance(data_label, np.ndarray)
        self.data_label = data_label

    def classify(self, inx):
        #输入一列，输出对应结果
        if self.isNorm:
            inx = (np.array(inx) - self.min_val) / self.sub_val
        n = self.data_mat.shape[0]
        import operator
        # d = ( (x1-x)**2 + (x2-x)**2 ...)**0.5
        distances = ((np.tile(inx, (n, 1)) - self.data_mat) ** 2).sum(axis=1) ** 0.5
        sort_args = distances.argsort()[:self.k]
        sort_labels = self.data_label[sort_args]
        count = {}
        for i in list(sort_labels):
            count[i] = count.get(i, 0) + 1
        sorted_count = sorted(count.iteritems(), key=operator.itemgetter(1), reverse=True)
        return sorted_count[0][0]

    def auto_norm(self):
        #对数据进行归一化
        self.min_val = self.data_mat.min(0)
        self.sub_val = self.data_mat.max(0) - self.min_val
        n = self.data_mat.shape[0]
        min_val = np.tile(self.min_val, (n, 1))
        sub_val = np.tile(self.sub_val, (n, 1))
        self.data_mat = (self.data_mat - min_val) / sub_val
        self.isNorm = True


# ##############################################################################
import unittest


class TestKnn(unittest.TestCase):
    def test_1(self):
        data_mat = np.array([[4.09200000e+04, 8.32697582e+00, 9.53952014e-01],
                             [1.44880000e+04, 7.15346909e+00, 1.67390394e+00],
                             [2.60520000e+04, 1.44187105e+00, 8.05123985e-01],
                             [7.51360000e+04, 1.31473942e+01, 4.28963989e-01],
                             [3.83440000e+04, 1.66978800e+00, 1.34296000e-01],
                             [7.29930000e+04, 1.01417398e+01, 1.03295505e+00],
                             [3.59480000e+04, 6.83079195e+00, 1.21319199e+00],
                             [4.26660000e+04, 1.32763691e+01, 5.43879986e-01],
                             [6.74970000e+04, 8.63157654e+00, 7.49278009e-01],
                             [3.54830000e+04, 1.22731686e+01, 1.50805295e+00],
                             [5.02420000e+04, 3.72349811e+00, 8.31916988e-01],
                             [6.32750000e+04, 8.38587856e+00, 1.66948497e+00],
                             [5.56900000e+03, 4.87543488e+00, 7.28658020e-01],
                             [5.10520000e+04, 4.68009806e+00, 6.25223994e-01]], dtype=np.float32)
        data_label = np.array(['3', '2', '1', '1', '1', '1', '3', '3', '1',
                               '3', '1', '1', '2', '1'])
        c = Classify(1)
        c.set_data_label(data_label)
        c.set_data_mat(data_mat)
        self.assertEqual(c.classify([4.09200000e+04, 8.32697582e+00, 9.53952014e-01]),
                         '3')
        c.auto_norm()

        self.assertEqual(c.classify([4.09200000e+04, 8.32697582e+00, 9.53952014e-01]),
                         '3')

    def test_2(self):
        data_mat = np.array([[10, 10, 5],
                             [20, 20, 20],
                             [3, 100, 0]], dtype=np.float32)
        data_label = np.array(['5', '20', '0'])
        c = Classify(1)
        c.set_data_label(data_label)
        c.set_data_mat(data_mat)
        c.auto_norm()
        self.assertEqual(c.classify([10, 10, 5]), '5')
        self.assertEqual(c.classify([14, 10, 5]), '5')
        self.assertEqual(c.classify([10, 11, 5]), '5')


if __name__ == '__main__':
    unittest.main()





